#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time       : 2025/8/7 10:45
# @Author     : cfg
# @File       : game_utils.py
# @Software   : PyCharm
# @Description: The description of this file.
import copy
import json
import os
from typing import List

import pygame

from sprites.bean import Bean
from sprites.role.snake import Snake
from sprites.role.snake_body import SnakeBody
from sprites.role.snake_header import SnakeHeader
from sprites.wall import Wall
from statics.color import Color
from statics.direction import Direction
from statics.font import Font
from statics.game_variable import GameVariable
from statics.text import Text
from utils.secret_utils import str2bin, bin2str


def died_judge(players: List[Snake], wall_list):
    """
    死亡判定
    :param players: 玩家列表
    :param wall_list: 墙体列表
    :return:
    """
    if GameVariable.player_num == 2:
        if GameVariable.players[0].is_died and GameVariable.players[1].is_died:
            GameVariable.current_page = GameVariable.died_page
        if GameVariable.players[0].is_died and not GameVariable.players[1].is_died:
            GameVariable.current_message = "玩家二获胜！"
            GameVariable.current_page = GameVariable.success_page
        if not GameVariable.players[0].is_died and GameVariable.players[1].is_died:
            GameVariable.current_message = "玩家一获胜！"
            GameVariable.current_page = GameVariable.success_page

    for idx, player in enumerate(players):
        # collide with self
        collide_index = fifth_part_collide_list(player.header.rect, player.body_rects)
        if collide_index != -1 and player.length == len(player.passed_coordinates):
            print("collide with self")
            player.died()
            if GameVariable.player_num == 1:
                GameVariable.current_page = GameVariable.died_page

        # collide with walls
        if player.header.rect.collidelist(wall_list) != -1:
            print("collide head with wall")
            player.died()
            if GameVariable.player_num == 1:
                GameVariable.current_page = GameVariable.died_page

        # collide with other player
        for j, op in enumerate(players):
            if idx == j:
                continue
            # collide with body
            if player.header.rect.collidelist(op.body_rects) != -1:
                if fifth_part_collide_list(player.header.rect, op.body_rects[-5:]) != -1:
                    print("collide head with body")
                    player.died()
                if player.header.rect.collidelist(op.body_rects[:-5]):
                    print("collide head with body")
                    player.died()

            # collide with head
            if player.header.rect.colliderect(op.header.rect):
                print("collide head with head")
                if player.length > op.length:
                    op.died()
                elif player.length < op.length:
                    player.died()
                else:
                    player.died()
                    op.died()


def fifth_part_collide_rect(rect1: pygame.rect.Rect, rect2: pygame.rect.Rect):
    flag = False
    if rect1.colliderect(rect2):
        rect1_copy = copy.deepcopy(rect1)
        rect1_copy.width = rect1.width / 5
        rect1_copy.height = rect1.height / 5
        rect2_copy = copy.deepcopy(rect2)
        rect2_copy.width = rect2.width / 5
        rect2_copy.height = rect2.height / 5
        if rect1_copy.colliderect(rect2_copy):
            flag = True
        del rect1_copy, rect2_copy
    return flag


def fifth_part_collide_list(rect1: pygame.rect.Rect, rect_list: List[pygame.rect.Rect]):
    collide_index_list = []
    if rect1.collidelist(rect_list) != -1:
        r1_copy = copy.deepcopy(rect1)
        r1_copy.width = rect1.width / 5
        r1_copy.height = rect1.height / 5

        for idx, ob_rect in enumerate(rect_list):
            ob_rect_copy = copy.deepcopy(ob_rect)
            ob_rect_copy.width = ob_rect.width / 5
            ob_rect_copy.height = ob_rect.height / 5
            if r1_copy.colliderect(ob_rect_copy):
                collide_index_list.append(idx)
            del ob_rect_copy
        del r1_copy
    if len(collide_index_list) > 0:
        return collide_index_list
    return -1


def pause_page_key_listener(key):
    if key in [pygame.K_p, pygame.K_ESCAPE]:
        GameVariable.paused = False
    if key in [pygame.K_DOWN, pygame.K_s]:
        if GameVariable.pause_cursor_line < 3:
            GameVariable.pause_cursor_line += 1
        else:
            GameVariable.pause_cursor_line = 1
    if key in [pygame.K_UP, pygame.K_w]:
        if GameVariable.pause_cursor_line > 1:
            GameVariable.pause_cursor_line -= 1
        else:
            GameVariable.pause_cursor_line = 3
    if key in [pygame.K_RETURN, pygame.K_KP_ENTER]:
        handle_pause_menu_chose(GameVariable.pause_cursor_line)


def handle_pause_menu_chose(chose_line: int):
    """
    暂停页面键盘点击 enter 事件处理
    :param chose_line: 光标所在行数
    :return:
    """
    if chose_line == 1:
        GameVariable.paused = False
        GameVariable.current_page = GameVariable.game_page
    if chose_line == 2:
        save_data()
        GameVariable.current_message = "保存完成！"
    if chose_line == 3:
        GameVariable.paused = False
        go_home()


def create_players():
    """
    创建玩家
    :return:
    """
    if GameVariable.player_num == 1:
        s = Snake(60, 120, header_color=Color.player1)
        GameVariable.players = [s]
    if GameVariable.player_num == 2:
        s1 = Snake(60, 120, header_color=Color.player1)
        s2 = Snake(60, 180, header_color=Color.player2)
        GameVariable.players = [s1, s2]


def create_walls(level: int = 1):
    """
    创建墙体
    :param level: 当前关卡，默认为 1
    :return:
    """
    wall1 = Wall(0, 0, 10, 100, Color.wall)
    wall2 = Wall(0, 0, 800, 10, Color.wall)
    wall3 = Wall(790, 0, 10, 100, Color.wall)
    wall4 = Wall(0, 100, 10, 500, Color.wall)
    wall5 = Wall(0, 100, 800, 10, Color.wall)
    wall6 = Wall(790, 100, 10, 500, Color.wall)
    wall7 = Wall(0, 590, 800, 10, Color.wall)
    wall_list = [wall1, wall2, wall3, wall4, wall5, wall6, wall7]

    if GameVariable.player_num == 1:
        wall8 = Wall(400, 0, 10, 100, Color.wall)
        wall_list.append(wall8)
    if GameVariable.player_num == 2:
        wall8 = Wall(266, 0, 10, 100, Color.wall)
        wall9 = Wall(523, 0, 10, 100, Color.wall)
        wall_list.append(wall8)
        wall_list.append(wall9)

    # todo: draw obstacle
    if level == 1:
        wall10 = Wall(150, 200, 100, 10, Color.wall)
        wall11 = Wall(550, 200, 100, 10, Color.wall)
        wall12 = Wall(150, 200, 10, 50, Color.wall)
        wall13 = Wall(650, 200, 10, 50, Color.wall)
        wall14 = Wall(150, 450, 100, 10, Color.wall)
        wall15 = Wall(550, 450, 100, 10, Color.wall)
        wall16 = Wall(150, 400, 10, 50, Color.wall)
        wall17 = Wall(650, 400, 10, 60, Color.wall)
        wall_list += [wall10, wall11, wall12, wall13, wall14, wall15, wall16, wall17]
    if level == 2:
        wall10 = Wall(320, 200, 60, 10, Color.wall)
        wall11 = Wall(430, 200, 60, 10, Color.wall)

        wall12 = Wall(380, 210, 10, 10, Color.wall)
        wall13 = Wall(390, 220, 10, 10, Color.wall)
        wall14 = Wall(400, 230, 10, 10, Color.wall)
        wall15 = Wall(420, 210, 10, 10, Color.wall)
        wall16 = Wall(410, 220, 10, 10, Color.wall)

        wall17 = Wall(310, 210, 10, 10, Color.wall)
        wall18 = Wall(490, 210, 10, 10, Color.wall)
        wall19 = Wall(300, 220, 10, 10, Color.wall)
        wall20 = Wall(500, 220, 10, 10, Color.wall)
        wall21 = Wall(290, 230, 10, 80, Color.wall)
        wall22 = Wall(510, 230, 10, 80, Color.wall)

        wall23 = Wall(140, 400, 250, 10, Color.wall)
        wall24 = Wall(420, 400, 250, 10, Color.wall)
        wall25 = Wall(140, 400, 10, 60, Color.wall)
        wall26 = Wall(660, 400, 10, 60, Color.wall)
        wall27 = Wall(140, 460, 250, 10, Color.wall)
        wall28 = Wall(420, 460, 250, 10, Color.wall)
        wall_list += [wall10, wall11, wall12, wall13, wall14, wall15, wall16, wall17, wall18, wall19, wall20, wall21,
                      wall22, wall23, wall24, wall25, wall26, wall27, wall28]
        for i in range(9):
            x = (i + 1) * 10 + 290
            y = (i + 1) * 10 + 300
            w = Wall(x, y, 10, 10, Color.wall)
            wall_list.append(w)

        for i in range(9):
            x = (10 - i) * 10 + 400
            y = 310 + i * 10
            w = Wall(x, y, 10, 10, Color.wall)
            wall_list.append(w)
    GameVariable.walls = wall_list


def create_beans():
    """
    创建豆子（随机创建10个）
    :return:
    """
    bean_list = []
    for i in range(3):
        b = Bean(20, 120)
        b.spawn(GameVariable.players, GameVariable.walls)
        bean_list.append(b)
    GameVariable.beans = bean_list


def game_page_key_listener(key):
    """
    游戏界面的键盘事件监听
    :param key: 键盘事件
    :return:
    """
    if key in [pygame.K_p, pygame.K_ESCAPE]:
        GameVariable.pause_cursor_line = 1
        GameVariable.paused = True
    players_control(0, key, pygame.K_w, pygame.K_s, pygame.K_a, pygame.K_d)
    if GameVariable.player_num == 2:
        players_control(1, key, pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT, pygame.K_RIGHT)


def players_control(player_index, key, k_up, k_down, k_left, k_right):
    """
    玩家控制器
    :param player_index: 玩家索引
    :param key: 键盘事件
    :param k_up: 定义向上的按键
    :param k_down: 定义向下的按键
    :param k_left: 定义向左的按键
    :param k_right: 定义向右的按键
    :return:
    """
    if key == k_up:
        if GameVariable.players[player_index].direction != Direction.down:
            GameVariable.players[player_index].direction = Direction.up
    if key == k_left:
        if GameVariable.players[player_index].direction != Direction.right:
            GameVariable.players[player_index].direction = Direction.left
    if key == k_down:
        if GameVariable.players[player_index].direction != Direction.up:
            GameVariable.players[player_index].direction = Direction.down
    if key == k_right:
        if GameVariable.players[player_index].direction != Direction.left:
            GameVariable.players[player_index].direction = Direction.right


def show_message(screen, text):
    surface = pygame.surface.Surface((300, 200))
    pygame.draw.rect(surface, Color.wall, (0, 0, 300, 200), 5)
    pygame.draw.rect(surface, (38, 38, 38), (5, 5, 290, 190))
    text_success = Font.message.render(text, True, Color.white)
    surface.blit(text_success, (120, 90))
    surface.blit(Text.text_any_key_continue, (150, 170))
    screen.blit(surface, (255, 180))


def handle_chose_one_player():
    GameVariable.player_num = 1
    GameVariable.current_page = GameVariable.game_page
    create_players()
    create_walls(1)
    create_beans()


def handle_chose_two_player():
    GameVariable.player_num = 2
    GameVariable.current_page = GameVariable.game_page
    create_players()
    create_walls(1)
    create_beans()


def handle_home_menu_chose(menu_num: int, cursor_line: int):
    # not exists saved data
    if menu_num == 4:
        # chose one player
        if cursor_line == 1:
            handle_chose_one_player()

        # chose two players
        if cursor_line == 2:
            handle_chose_two_player()

        # chose readme
        if cursor_line == 3:
            GameVariable.current_page = GameVariable.readme_page

        # chose exit
        if cursor_line == 4:
            GameVariable.exited = True
    # exists saved data
    if menu_num == 5:
        # chose continue
        if cursor_line == 1:
            restore_save_data()

        # chose one player
        if cursor_line == 2:
            handle_chose_one_player()

        # chose two players
        if cursor_line == 3:
            handle_chose_two_player()

        # chose readme
        if cursor_line == 4:
            GameVariable.current_page = GameVariable.readme_page

        # chose exit
        if cursor_line == 5:
            GameVariable.exited = True


def save_data():
    data = {}
    player_dicts = []
    for pi, player in enumerate(GameVariable.players):
        body_centers = []
        for idx, body_rect in enumerate(player.body_rects):
            body_center = body_rect.center
            body_centers.append(body_center)
        player_dict = {
            "header_center": player.header.rect.center,
            "body_centers": body_centers,
            "length": player.length,
            "direction": player.direction,
            "header_color": player.header_color,
            "body_color": player.body_color,
        }
        player_dicts.append(player_dict)

    bean_dicts = []
    for bi, bean in enumerate(GameVariable.beans):
        bean_dict = {
            "color": bean.color,
            "center": bean.rect.center
        }
        bean_dicts.append(bean_dict)
    data["players"] = player_dicts
    data["beans"] = bean_dicts
    data["level"] = GameVariable.current_level
    data["player_num"] = GameVariable.player_num
    data["player1_score"] = GameVariable.player1_score
    data["player2_score"] = GameVariable.player2_score
    data_text = json.dumps(data)

    with open("save_data.dat", "w", encoding="gbk") as f:
        f.write(str2bin(data_text))


def restore_save_data():
    """
    恢复存档数据
    :return:
    """
    if os.path.exists("save_data.dat"):
        with open("save_data.dat", "r", encoding="gbk") as f:
            data = f.read()
            GameVariable.saved_datas = [json.loads(bin2str(data))]

    saved_data = GameVariable.saved_datas[0]

    # restore players
    players_json = saved_data["players"]
    players = []
    for player_json in players_json:
        body_centers = []
        header_center = player_json["header_center"]
        header_color = player_json["header_color"]
        body_color = player_json["body_color"]
        length = player_json["length"]
        direction = player_json["direction"]
        header = SnakeHeader(header_color)
        header.rect.center = header_center
        bodies = []
        body_rects = []
        for body_center_str in player_json["body_centers"]:
            body_center = body_center_str
            body_centers.append(body_center)

            body = SnakeBody(body_color)
            body.rect.center = body_center
            bodies.append(body)
            body_rects.append(body.rect)

        player = Snake(header_center[0], header_center[1], header_color, body_color)
        player.header = header
        player.bodies = bodies
        player.length = length
        player.direction = direction
        player.body_rects = body_rects
        player.passed_coordinates = body_centers
        players.append(player)
    GameVariable.players = players

    # restore beans
    beans = []
    for bean_json in saved_data["beans"]:
        bean_color = bean_json["color"]
        bean_center = bean_json["center"]
        bean = Bean(bean_center[0], bean_center[1], bean_color)
        beans.append(bean)
    GameVariable.beans = beans

    # restore other variable
    GameVariable.current_level = saved_data["level"]
    GameVariable.current_page = GameVariable.game_page
    GameVariable.player_num = saved_data["player_num"]
    GameVariable.player1_score = saved_data["player1_score"]
    GameVariable.player2_score = saved_data["player2_score"]

    # restore walls
    create_walls(GameVariable.current_level)


def key_listener(key):
    if not GameVariable.current_message:
        # home page keydown listener
        if GameVariable.current_page == GameVariable.home_page:
            home_page_key_listener(key)
            return
        # game page keydown listener
        if GameVariable.current_page == GameVariable.game_page:
            game_page_key_listener(key)
            return
        if GameVariable.current_page == GameVariable.readme_page:
            GameVariable.current_page = GameVariable.home_page
            return
        if GameVariable.current_page == GameVariable.success_page:
            success_page_key_listener(key)
            return
        if GameVariable.current_page == GameVariable.died_page:
            died_page_key_listener(key)
            return
    else:
        GameVariable.current_message = ""


def home_page_key_listener(key):
    if key in [pygame.K_DOWN, pygame.K_s]:
        if GameVariable.home_cursor_line < GameVariable.home_menu_num:
            GameVariable.home_cursor_line += 1
        else:
            GameVariable.home_cursor_line = 1

    if key in [pygame.K_UP, pygame.K_w]:
        if GameVariable.home_cursor_line > 1:
            GameVariable.home_cursor_line -= 1
        else:
            GameVariable.home_cursor_line = GameVariable.home_menu_num

    if key in [pygame.K_RETURN, pygame.K_KP_ENTER]:
        handle_home_menu_chose(GameVariable.home_menu_num, GameVariable.home_cursor_line)


def success_page_key_listener(key):
    if key in [pygame.K_DOWN, pygame.K_s]:
        if GameVariable.success_cursor_line < 3:
            GameVariable.success_cursor_line += 1
        else:
            GameVariable.success_cursor_line = 1

    if key in [pygame.K_UP, pygame.K_w]:
        if GameVariable.success_cursor_line > 1:
            GameVariable.success_cursor_line -= 1
        else:
            GameVariable.success_cursor_line = 3

    if key in [pygame.K_RETURN, pygame.K_KP_ENTER]:
        handle_success_menu_chose(GameVariable.success_cursor_line)


def handle_success_menu_chose(chose_line: int):
    """
    过关成功界面菜单选择
    :param chose_line: 选择的行
    :return:
    """
    if chose_line == 1:
        GameVariable.current_level += 1
        GameVariable.player1_score = 0
        GameVariable.player2_score = 0

        if GameVariable.player_num == 2:
            s1 = Snake(60, 120, header_color=Color.player1)
            s2 = Snake(60, 180, header_color=Color.player2)
            GameVariable.players = [s1, s2]
        # for idx, player in enumerate(GameVariable.players):
        #     if idx == 0:
        #         player.reset((60, 120), Color.player1)
        #     if idx == 1:
        #         player.reset((60, 180), Color.player2)
        create_walls(GameVariable.current_level)
        create_beans()
        GameVariable.current_page = GameVariable.game_page
    if chose_line == 2:
        GameVariable.current_level += 1
        GameVariable.player1_score = 0
        GameVariable.player2_score = 0
        for player in GameVariable.players:
            player.reset()
        create_walls(GameVariable.current_level)
        create_beans()
        save_data()
        GameVariable.current_level -= 1
        GameVariable.current_message = "保存完成！"
    if chose_line == 3:
        go_home()


def died_page_key_listener(key):
    if key in [pygame.K_DOWN, pygame.K_s]:
        if GameVariable.died_cursor_line < 2:
            GameVariable.died_cursor_line += 1
        else:
            GameVariable.died_cursor_line = 1
    if key in [pygame.K_UP, pygame.K_w]:
        if GameVariable.died_cursor_line > 1:
            GameVariable.died_cursor_line -= 1
        else:
            GameVariable.died_cursor_line = 2
    if key in [pygame.K_RETURN, pygame.K_KP_ENTER]:
        handle_died_menu_chose(GameVariable.died_cursor_line)


def handle_died_menu_chose(cursor_line: int):
    if cursor_line == 1:
        GameVariable.player1_score = 0
        GameVariable.player2_score = 0
        for player in GameVariable.players:
            player.reset()
        create_walls(GameVariable.current_level)
        create_beans()
        GameVariable.current_page = GameVariable.game_page
    if cursor_line == 2:
        go_home()


def go_home():
    GameVariable.player1_score = 0
    GameVariable.player2_score = 0
    for player in GameVariable.players:
        player.reset()
    GameVariable.current_level = 1
    GameVariable.current_page = GameVariable.home_page


def is_passed():
    if GameVariable.player_num == 1:
        if GameVariable.current_level == 1:
            if GameVariable.player1_score >= GameVariable.level1_pass_score:
                return True
            return False
        if GameVariable.current_level == 2:
            if GameVariable.player1_score >= GameVariable.level2_pass_score:
                return True
            return False
        if GameVariable.current_level == 3:
            if GameVariable.player1_score >= GameVariable.level3_pass_score:
                return True
            return False
    if GameVariable.player_num == 2:
        for player in GameVariable.players:
            if player.is_died:
                return True
        return False
    return False
